iT邦幫忙

2022 iThome 鐵人賽

DAY 15
0

做為一個開發人員,不管是在維護舊code或是開發新功能的時候常常或有兩個情況

還有一種是QA轉頭就冒一句,欸為什麼這個功能壞掉了
這時候的心理OS是,我又不是開發這個功能的人,你問我不如擲筊還比較準。
當然,心裡想的是一套,實際上還是得要使用通靈之術,從各種蛛絲馬跡中找出可能的原因。
這時候,良好的log就派上用場了。
好的log可以降低你通靈的時間,快速的對焦出問題根源

現在讓我們來看看.Net 中的log機制如何運作的吧

log方式

這邊介紹的是原生的log 方式

Debugger

指的是當應用程式附加到當前偵錯工具(ex. ide(visual studio))時,可以透過Debugger.Log 的方式來log Debugger.Log
在我現在的公司,滿常使用attach to process 將iis process 掛到當前ide去做debug的,這時候debugger算是不錯的log工具(雖然下中斷點比較快)。
https://ithelp.ithome.com.tw/upload/images/20220926/20109549hFv7Woym34.png

也可以用Debug.WriteLine()的寫法

TraceSource

通常一個log事件可以拆分為

  • 事件Id
  • 事件類型(事件等級)
  • 訊息

TraceSource透過Enum TraceEventType 來定義其等級,Crtical 最嚴重,Resume最不重要

 public enum TraceEventType
  {
    /// <summary>Fatal error or application crash.</summary>
    Critical = 1,
    /// <summary>Recoverable error.</summary>
    Error = 2,
    /// <summary>Noncritical problem.</summary>
    Warning = 4,
    /// <summary>Informational message.</summary>
    Information = 8,
    /// <summary>Debugging trace.</summary>
    Verbose = 16, // 0x00000010
    /// <summary>Starting of a logical operation.</summary>
    Start = 256, // 0x00000100
    /// <summary>Stopping of a logical operation.</summary>
    Stop = 512, // 0x00000200
    /// <summary>Suspension of a logical operation.</summary>
    Suspend = 1024, // 0x00000400
    /// <summary>Resumption of a logical operation.</summary>
    Resume = 2048, // 0x00000800
    /// <summary>Changing of correlation identity.</summary>
    Transfer = 4096, // 0x00001000
  }

https://ithelp.ithome.com.tw/upload/images/20220926/20109549wnz3LXGlgL.png

// SourceLevels.All表示最低紀錄層級為全部
var traceSource = new TraceSource("my_event", SourceLevels.All);

另外,TraceSource 是採用觀察者模式,也就是可以透過訂閱TraceSource來處理 Log
原先在Debug Console有東西是因為IDE會自動幫你註冊一個Listener。

var traceSource = new TraceSource("my_event", SourceLevels.All);
var listener = new Listener();
traceSource.Listeners.Add(listener);

traceSource.TraceEvent(TraceEventType.Critical, 1, "密碼已輸入錯誤3次,即將啟動自爆裝置");
traceSource.TraceEvent(TraceEventType.Information, 2, "自爆倒數: 5...");
traceSource.TraceEvent(TraceEventType.Information, 2, "自爆倒數: 4...");
traceSource.TraceEvent(TraceEventType.Information, 2, "自爆倒數: 3...");
traceSource.TraceEvent(TraceEventType.Information, 2, "自爆倒數: 2...");
traceSource.TraceEvent(TraceEventType.Information, 2, "自爆倒數: 1...");
traceSource.TraceEvent(TraceEventType.Error, 3, "權限不足,自爆失敗");

class Listener: TraceListener
{
    public override void Write(string message)
    {
        Console.Write(message);
    }

    public override void WriteLine(string message)
    {
        Console.WriteLine(message);
    }
}

原先只在debug console出現的文字,透過自定義管道輸出到console中

EventSource

微軟文件
一樣是走觀察者模式,所以同樣會有log的發布者,與決定要如何輸出的訂閱者。

HostEventSource.cs 發布者

class HostEventSource : EventSource
{
    public static HostEventSource Log = new HostEventSource();

    [Event(1, Level = EventLevel.Informational)]
    public void Start(string msg)
    {
        WriteEvent(1, msg);
    }

    [Event(2, Level = EventLevel.Informational)]
    public void Stop(string msg)
    {
        WriteEvent(2, msg);
    }
    
    [Event(3, Level = EventLevel.Critical)]
    public void Shutdown( string message)
    {
        WriteEvent(3, message);
    }
}

若無指定eventId,系統由1遞增,自動幫忙加

Listener.cs

class Listener: EventListener
{
    protected override void OnEventWritten(EventWrittenEventArgs eventData)
    {
        Console.WriteLine(eventData.EventName);
        Console.WriteLine(eventData.Payload?[0]);
    }
}

Program.cs

var listener = new Listener();
listener.EnableEvents(HostEventSource.Log, EventLevel.Informational);
HostEventSource.Log.Start("Host started");
HostEventSource.Log.Shutdown("Host shutting down");
HostEventSource.Log.Stop("Host stopped");

https://ithelp.ithome.com.tw/upload/images/20220926/20109549I13LPTyHDy.png

DiagnosticSource

微軟文件

同樣還是觀察者模式,實作了IObservable<T>IObserver<T> 的觀察者介面

Observer.cs

class Observer<T> : IObserver<T>
{
    public Observer(Action<T> onNext, Action onCompleted)
    {
        _onNext = onNext ?? new Action<T>(_ => { });
        _onCompleted = onCompleted ?? new Action(() => { });
    }
    public void OnCompleted() { _onCompleted(); }
    public void OnError(Exception error) { }
    public void OnNext(T value) { _onNext(value); }
    private Action<T> _onNext;
    private Action _onCompleted;
}

Program.cs

using System.Diagnostics;
using System.Text.Json;

DiagnosticListener.AllListeners.Subscribe(new Observer<DiagnosticListener>(listener =>
    {
        IObserver<KeyValuePair<string, object>> observer = new Observer<KeyValuePair<string, object>>(x =>
            {
                Console.WriteLine(x.Key);
                Console.WriteLine(JsonSerializer.Serialize(x.Value));
            },
            () => { });
        listener.Subscribe(observer!);
    },
    () => { }));

var diagnosticListener = new DiagnosticListener("Host");
diagnosticListener.Write("Start", "HostStarting");

上一篇
[Day14] 選項模式(2) - Configuration - 5
下一篇
[Day16] .Net Core 中的Log - 2
系列文
擁抱 .Net Core30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言